<?php
/**
 * 观察者模式
 * 又称发布-订阅模式
 * 观察者观察被观察者，被观察者通知观察者
 * 
 */
spl_autoload_register('autoload');

function autoload($class) 
{
    require dirname($_SERVER['SCRIPT_FILENAME']) . '//..//' . str_replace('\\', '/', $class) . '.php';
}

/*============================= 测试 =============================*/

use observer\LoginObservable;
use observer\FriendObserver;
use observer\ActivityObserver;
use observer\LogObserver;

//创建被观察者，此处为用户登录行为
$loginObservable = new LoginObservable();

//创建观察者
$friendObserver = new FriendObserver(); //观察者1，即业务1：推荐好友关注
$activityObserver = new ActivityObserver(); //观察者2，即业务2：推荐线下交友活动
$logObserver = new LogObserver(); //观察者3，即业务3：记录登录日志

//添加观察者到名单列表
//另一种办法：不一定要添加这个名单，可以直接把相应的类的方法委托给一个单独的类处理，委托可以理解成是对函数的抽象（大话设计模式P139）
$loginObservable->addObserver($friendObserver); //subscribe订阅
$loginObservable->addObserver($activityObserver);
$loginObservable->addObserver($logObserver);

//移除一个观察者 试试
$loginObservable->deleteObserver($logObserver);

//用户登录，发送通知
$loginObservable->notify(); //publish发布

/*
小结：
观察者模式的应用场景：
当一个对象改变时需要同时改变其他对象，且其他对象的个数动态可变。
当一个事件发生后，要执行一连串更新操作。

优点：
1、一个抽象模型有两个方面，其中一个方面依赖另一个方面，这时用观察者模式可以将这两者封装在独立的对象中
   使他们各自独立地改变和复用
2、观察者模式主要工作其实就是解耦，让耦合的双方都依赖于抽象，而不是依赖于具体，从而使得各自的变化
   都不会影响另一边的变化（依赖倒转原则）。
缺点：
1、如果观察者过多，会花费更多时间，也可以考虑起一个单独的线程来异步处理
2、如果在被观察者之间有循环依赖的话，被观察者会触发它们之间进行循环调用，导致系统崩溃
3、观察者知道被观察者的变化，但不知道观察者发生变化的原因是什么。

个人感觉观察者模式和装饰器模式有点像
待研究：
https://segmentfault.com/a/1190000010799627
https://www.jianshu.com/p/aa733b7032d2
*/
